﻿using MudEngine.Collections;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MudEngine.Runtime
{
    public class Value
    {
        /// <summary>
        /// 
        /// </summary>
        private ValueType _type;
        /// <summary>
        /// 
        /// </summary>
		private long _number;
        /// <summary>
        /// 
        /// </summary>
        private decimal _decimal;
        /// <summary>
        /// 
        /// </summary>
        private string _string;
        /// <summary>
        /// 
        /// </summary>
        private object_t _object;
        /// <summary>
        /// 
        /// </summary>
        private Mapping _map;
        /// <summary>
        /// 
        /// </summary>
        private ValueArray _array;
        /// <summary>
        /// 
        /// </summary>
        public static Value Null = new Value();
        /// <summary>
        /// 
        /// </summary>
		public static Value constI1_0 = new Value((byte)0);
        /// <summary>
        /// 
        /// </summary>
		public static Value constI1_1 = new Value((byte)1);
        /// <summary>
        /// 
        /// </summary>
		public static Value constI2_0 = new Value((short)0);
        /// <summary>
        /// 
        /// </summary>
		public static Value constI2_1 = new Value((short)1);
        /// <summary>
        /// 
        /// </summary>
		public static Value constI4_0 = new Value((int)0);
        /// <summary>
        /// 
        /// </summary>
		public static Value constI4_1 = new Value((int)1);
        /// <summary>
        /// 
        /// </summary>
        public Value()
        {
            this._type = ValueType.INVALID;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="val"></param>
		public Value(byte val)
		{
			this._type = ValueType.BOOL;
			this._number = val;
		}
        /// <summary>
        /// 
        /// </summary>
        /// <param name="val"></param>
		public Value(short val)
		{
			this._type = ValueType.SHORT;
			this._number = val;
		}
        /// <summary>
        /// 
        /// </summary>
        /// <param name="val"></param>
		public Value(int val)
		{
			this._type = ValueType.INT;
			this._number = val;
		}
        /// <summary>
        /// 
        /// </summary>
        /// <param name="val"></param>
		public Value(long val)
		{
			this._type = ValueType.LONG;
			this._number = val;
		}
        /// <summary>
        /// 
        /// </summary>
        /// <param name="val"></param>
		public Value(string val)
		{
			this._type = ValueType.STRING;
			this._string = val;
		}
        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
		public Value(object_t obj)
		{
			this._type = ValueType.OBJECT;
			this._object = obj;
		}
        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        public Value(Mapping obj)
        {
            this._type = ValueType.MAP;
            this._map = obj;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        public Value(ValueArray obj)
        {
            this._type = ValueType.ARRAY;
            this._array = obj;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        public Value(object obj)
		{
			if (obj is object_t)
			{
				this._type = ValueType.OBJECT;
				this._object = (object_t)obj;
			}
			else if (obj is string)
			{
				this._type = ValueType.STRING;
				this._string = (string)obj;
			}
			else if (obj is long)
			{
				this._type = ValueType.LONG;
				this._number = (long)obj;
			}
			else if (obj is int)
			{
				this._type = ValueType.INT;
				this._number = (int)obj;
			}
			else if (obj is short)
			{
				this._type = ValueType.SHORT;
				this._number = (short)obj;
			}
			else if (obj is byte)
			{
				this._type = ValueType.BOOL;
				this._number = (byte)obj;
			}
            else if (obj is ValueArray)
            {
                this._type = ValueType.ARRAY;
                this._array = (ValueArray)obj;
            }
            else if (obj is Mapping)
            {
                this._type = ValueType.MAP;
                this._map = (Mapping)obj;
            }
            else
				throw new NotImplementedException("Unsupported value in Value constructor.");
		}
        /// <summary>
        /// 
        /// </summary>
		public ValueType Type
		{
			get
			{
				return this._type;
			}
		}
        /// <summary>
        /// 
        /// </summary>
		public byte Bool
		{
			get
			{
				if (_type != ValueType.BOOL)
					return 0;
				return (byte)this._number;
			}
		}
        /// <summary>
        /// 
        /// </summary>
		public short Short
        {
            get
            {
                if (_type != ValueType.SHORT)
					return 0;
                return (short)this._number;
            }
        }
        /// <summary>
        /// 
        /// </summary>
		public int Int
		{
			get
			{
                if (_type != ValueType.BOOL && _type != ValueType.SHORT && _type != ValueType.INT)
					return 0;
				return (int)this._number;
			}
		}
        /// <summary>
        /// 
        /// </summary>
		public long Long
		{
			get
			{
				if (_type != ValueType.LONG)
					return 0;
				return this._number;
			}
		}
        /// <summary>
        /// 
        /// </summary>
		public decimal Decimal
        {
            get
            {
                if (_type != ValueType.DECIMAL)
					return 0;
                return this._decimal;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        public object_t Object
        {
            get
            {
                if (_type != ValueType.OBJECT)
                    return null;
                return this._object;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        public string String
        {
            get
            {
                if (_type != ValueType.STRING)
					return string.Empty;
                return this._string;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        public Mapping Map
        {
            get
            {
                if (_type != ValueType.MAP)
                    return null;
                return this._map;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        public ValueArray Array
        {
            get
            {
                if (_type != ValueType.ARRAY)
                    return null;
                return this._array;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        public bool IsNumber
        {
            get
            {
                return _type == ValueType.BOOL || _type == ValueType.SHORT || _type == ValueType.INT;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        public object Raw
        {
            get
            {
                switch (this.Type)
                {
                    case ValueType.BOOL: return this.Bool;
                    case ValueType.SHORT: return this.Short;
                    case ValueType.INT: return this.Int;
                    case ValueType.LONG: return this.Long;
                    case ValueType.STRING: return this.String;
                    case ValueType.OBJECT: return this.Object;
                    case ValueType.MAP: return this.Map;
                    case ValueType.ARRAY: return this.Array;
                    default: return null;
                }
            }
        }
		/// <summary>
		/// 
		/// </summary>
		/// <param name="val"></param>
		/// <returns></returns>
		public static implicit operator Value(bool val)
		{
			return val?Value.constI1_1:Value.constI1_0;
		}
        /// <summary>
        /// 
        /// </summary>
        /// <param name="val"></param>
        /// <returns></returns>
		public static implicit operator Value(short val)
		{
			return new Value(val);
		}
        /// <summary>
        /// 
        /// </summary>
        /// <param name="val"></param>
        /// <returns></returns>
		public static implicit operator Value(int val)
		{
			return new Value(val);
		}
        /// <summary>
        /// 
        /// </summary>
        /// <param name="val"></param>
        /// <returns></returns>
		public static implicit operator Value(long val)
		{
			return new Value(val);
		}
        /// <summary>
        /// 
        /// </summary>
        /// <param name="val"></param>
        /// <returns></returns>
		public static implicit operator Value(string val)
		{
			return new Value(val);
		}
        public static implicit operator string(Value val)
        {
            return val.String;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="val"></param>
        /// <returns></returns>
        public static implicit operator Value(object_t val)
        {
            return new Value(val);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="val"></param>
        /// <returns></returns>
        public static implicit operator Value(Mapping val)
        {
            return new Value(val);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="val"></param>
        /// <returns></returns>
        public static implicit operator Value(ValueArray val)
        {
            return new Value(val);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override string ToString()
		{
			switch (this._type)
			{
				case ValueType.BOOL:
                    return this._number == 0 ? "false" : "true";
				case ValueType.SHORT:
				case ValueType.INT:
				case ValueType.LONG:
					return this._number.ToString();
				case ValueType.STRING: return this._string;
                case ValueType.OBJECT: return string.Format("<{0}>", this._object.Name);
                case ValueType.MAP: return this._map.ToString();
                case ValueType.ARRAY: return this._array.ToString();
            }
			return "(Null)";
		}
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        internal bool IsFalseValue()
        {
            return !this.IsTrueValue();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        internal bool IsTrueValue()
        {
            switch (this._type)
            {
                case ValueType.BOOL:
                case ValueType.SHORT:
                case ValueType.INT:
                case ValueType.LONG:
                    return this._number != 0;
                case ValueType.DECIMAL:
                    return this._decimal != 0;
                case ValueType.STRING: return !string.IsNullOrEmpty(this._string);
                case ValueType.OBJECT: return this._object != null;
                case ValueType.MAP: return this._map.Count > 0;
                case ValueType.ARRAY: return this._array.Count > 0;
            }
            return true;
        }
        /// <summary>
		/// 
		/// </summary>
		/// <param name="v1"></param>
		/// <param name="v2"></param>
		/// <returns></returns>
		public static Value Ceq(Value v1, Value v2)
		{
			if (System.Object.ReferenceEquals(v1, v2))
				return Value.constI1_1;

			// If one is null, but not both, return false.
			if (v1 == null || v2 == null)
				return Value.constI1_0;

			bool result = false;
			switch (v1._type)
			{
                case ValueType.DECIMAL:
                    switch (v2.Type)
                    {
                        case ValueType.DECIMAL:
                            result = v1._decimal == v2._decimal;
                            break;
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            result = v1._decimal == v2._number;
                            break;
                        default:
                            throw new RuntimeException("Bad type argument to ==. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                    break;
                case ValueType.SHORT:
                case ValueType.INT:
                case ValueType.LONG:
                    switch (v2.Type)
                    {
                        case ValueType.DECIMAL:
                            result = v1._number == v2._decimal;
                            break;
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            result = v1._number == v2._number;
                            break;
                        default:
                            throw new RuntimeException("Bad type argument to ==. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                    break;
                case ValueType.STRING:
                    if (v2.Type == ValueType.STRING)
                        result = v1._string.Equals(v2._string);
                    else
                        throw new RuntimeException("Bad type argument to ==. Had {0} and {1}.", v1.Type, v2.Type);
                    break;
                case ValueType.MAP:
                    result = v1.Map.Equals(v2.Map);
                    break;
                case ValueType.ARRAY:
                    result = v1.Array.Equals(v2.Array);
                    break;
                default:
                    throw new RuntimeException("Bad type argument to ==. Had {0} and {1}.", v1.Type, v2.Type);
            }
			return result ? Value.constI1_1 : Value.constI1_0;
		}
        /// <summary>
        /// 
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        public static Value Cneq(Value v1, Value v2)
        {
            Value retValue = Ceq(v1, v2);
            if (retValue.Bool == 1)
                return Value.constI1_0;
            return Value.constI1_1;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        internal static Value Gt(Value v1, Value v2)
        {
            if (v1 == null || v2 == null)
                return Value.constI1_0;

            bool result = false;
            switch (v1._type)
            {
                case ValueType.DECIMAL:
                    switch (v2.Type)
                    {
                        case ValueType.DECIMAL:
                            result = v1._decimal > v2._decimal;
                            break;
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            result = v1._decimal > v2._number;
                            break;
                        default:
                            throw new RuntimeException("Bad type argument to >. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                    break;
                case ValueType.SHORT:
                case ValueType.INT:
                case ValueType.LONG:
                    switch (v2.Type)
                    {
                        case ValueType.DECIMAL:
                            result = v1._number > v2._decimal;
                            break;
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            result = v1._number > v2._number;
                            break;
                        default:
                            throw new RuntimeException("Bad type argument to >. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                    break;
                case ValueType.STRING:
                    if (v2.Type == ValueType.STRING)
                        result = v1._string.CompareTo(v2._string) > 0;
                    else
                        throw new RuntimeException("Bad type argument to >. Had {0} and {1}.", v1.Type, v2.Type);
                    break;
                default:
                    throw new RuntimeException("Bad type argument to >. Had {0} and {1}.", v1.Type, v2.Type);
            }
            return result ? Value.constI1_1 : Value.constI1_0;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        internal static Value Gte(Value v1, Value v2)
        {
            if (v1 == null || v2 == null)
                return Value.constI1_0;

            bool result = false;
            switch (v1._type)
            {
                case ValueType.DECIMAL:
                    switch (v2.Type)
                    {
                        case ValueType.DECIMAL:
                            result = v1._decimal >= v2._decimal;
                            break;
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            result = v1._decimal >= v2._number;
                            break;
                        default:
                            throw new RuntimeException("Bad type argument to >=. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                    break;
                case ValueType.SHORT:
                case ValueType.INT:
                case ValueType.LONG:
                    switch (v2.Type)
                    {
                        case ValueType.DECIMAL:
                            result = v1._number >= v2._decimal;
                            break;
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            result = v1._number >= v2._number;
                            break;
                        default:
                            throw new RuntimeException("Bad type argument to >=. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                    break;
                case ValueType.STRING:
                    if (v2.Type == ValueType.STRING)
                        result = v1._string.CompareTo(v2._string) >= 0;
                    else
                        throw new RuntimeException("Bad type argument to >=. Had {0} and {1}.", v1.Type, v2.Type);
                    break;
                default:
                    throw new RuntimeException("Bad type argument to >=. Had {0} and {1}.", v1.Type, v2.Type);
            }
            return result ? Value.constI1_1 : Value.constI1_0;
        }
        /// <summary>
		/// 
		/// </summary>
		/// <param name="v1"></param>
		/// <param name="v2"></param>
		/// <returns></returns>
		internal static Value Lt(Value v1, Value v2)
		{
			if (v1 == null || v2 == null)
				return Value.constI1_0;

			bool result = false;
			switch (v1._type)
			{
				case ValueType.DECIMAL:
                    switch (v2.Type)
                    {
                        case ValueType.DECIMAL:
                            result = v1._decimal < v2._decimal;
                            break;
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            result = v1._decimal < v2._number;
                            break;
                        default:
                            throw new RuntimeException("Bad type argument to <. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                    break;
				case ValueType.SHORT:
				case ValueType.INT:
				case ValueType.LONG:
                    switch (v2.Type)
                    {
                        case ValueType.DECIMAL:
                            result = v1._number < v2._decimal;
                            break;
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            result = v1._number < v2._number;
                            break;
                        default:
                            throw new RuntimeException("Bad type argument to <. Had {0} and {1}.", v1.Type, v2.Type);
                    }
					break;
				case ValueType.STRING:
                    if (v2.Type == ValueType.STRING)
                        result = v1._string.CompareTo(v2._string) < 0;
                    else
                        throw new RuntimeException("Bad type argument to <. Had {0} and {1}.", v1.Type, v2.Type);
                    break;
                default:
                    throw new RuntimeException("Bad type argument to <. Had {0} and {1}.", v1.Type, v2.Type);
			}
			return result ? Value.constI1_1 : Value.constI1_0;
		}
        /// <summary>
        /// 
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        internal static Value Lte(Value v1, Value v2)
        {
            if (v1 == null || v2 == null)
                return Value.constI1_0;

            bool result = false;
            switch (v1._type)
            {
                case ValueType.DECIMAL:
                    switch (v2.Type)
                    {
                        case ValueType.DECIMAL:
                            result = v1._decimal <= v2._decimal;
                            break;
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            result = v1._decimal <= v2._number;
                            break;
                        default:
                            throw new RuntimeException("Bad type argument to <=. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                    break;
                case ValueType.SHORT:
                case ValueType.INT:
                case ValueType.LONG:
                    switch (v2.Type)
                    {
                        case ValueType.DECIMAL:
                            result = v1._number <= v2._decimal;
                            break;
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            result = v1._number <= v2._number;
                            break;
                        default:
                            throw new RuntimeException("Bad type argument to <=. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                    break;
                case ValueType.STRING:
                    if (v2.Type == ValueType.STRING)
                        result = v1._string.CompareTo(v2._string) <= 0;
                    else
                        throw new RuntimeException("Bad type argument to <=. Had {0} and {1}.", v1.Type, v2.Type);
                    break;
                default:
                    throw new RuntimeException("Bad type argument to <=. Had {0} and {1}.", v1.Type, v2.Type);
            }
            return result ? Value.constI1_1 : Value.constI1_0;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
		internal static Value Sub(Value v1, Value v2)
		{
			if (v1 == null && v2 == null)
				return Value.constI1_0;
			if (v1 == null)
				return v2;
			if (v2 == null)
				return v1;

            switch (v1.Type)
            {
                case ValueType.SHORT:
                case ValueType.INT:
                case ValueType.LONG:
                    switch (v2.Type)
                    {
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            return new Value(v1._number - v2._number);
                        case ValueType.DECIMAL:
                            return new Value(v1._number - v2._decimal);
                        default:
                            throw new RuntimeException("Bad type argument to -. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                case ValueType.DECIMAL:
                    switch (v2.Type)
                    {
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            return new Value(v1._decimal - v2._number);
                        case ValueType.DECIMAL:
                            return new Value(v1._decimal - v2._decimal);
                        default:
                            throw new RuntimeException("Bad type argument to -. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                default:
                    throw new RuntimeException("Bad type argument to -. Had {0} and {1}.", v1.Type, v2.Type);
            }
		}
        /// <summary>
        /// 
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
		internal static Value Add(Value v1, Value v2)
		{
			if (v1 == null && v2 == null)
				return Value.constI1_0;
			if (v1 == null)
				return v2;
			if (v2 == null)
				return v1;

            switch (v1.Type)
            {
                case ValueType.ARRAY:
                    switch (v2.Type)
                    {
                        case ValueType.ARRAY:
                            foreach (Value val in v2.Array)
                                v1.Array.Add(val);
                            return v1;
                        case ValueType.BOOL:
                        case ValueType.DECIMAL:
                        case ValueType.INT:
                        case ValueType.LONG:
                        case ValueType.MAP:
                        case ValueType.OBJECT:
                        case ValueType.SHORT:
                        case ValueType.STRING:
                            v1.Array.Add(v2);
                            return (v1);
                        default:
                            throw new RuntimeException("Bad type argument to +. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                case ValueType.DECIMAL:
                    switch (v2.Type)
                    {
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            return new Value(v1._decimal + v2._number);
                        case ValueType.DECIMAL:
                            return new Value(v1._decimal + v2._decimal);
                        case ValueType.STRING:
                            return new Value(v1._decimal + v2._string);
                        default:
                            throw new RuntimeException("Bad type argument to +. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                case ValueType.SHORT:
                case ValueType.INT:
                    switch (v2.Type)
                    {

                        case ValueType.SHORT:
                        case ValueType.INT:
                            return new Value(v1.Int + v2.Int);
                        case ValueType.LONG:
                            return new Value(v1.Int + v2.Long);
                        case ValueType.DECIMAL:
                            return new Value(v1.Int + v2._decimal);
                        case ValueType.STRING:
                            return new Value(v1.Int + v2._string);
                        default:
                            throw new RuntimeException("Bad type argument to +. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                case ValueType.LONG:
                    switch (v2.Type)
                    {
                           
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            return new Value(v1._number + v2._number);
                        case ValueType.DECIMAL:
                            return new Value(v1._number + v2._decimal);
                        case ValueType.STRING:
                            return new Value(v1._number + v2._string);
                        default:
                            throw new RuntimeException("Bad type argument to +. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                case ValueType.MAP:
                    if (v2.Type == ValueType.MAP)
                    {
                        Mapping tm = v1.Map;
                        foreach (Value key in v2.Map.Keys)
                        {
                            if (!tm.ContainsKey(key))
                                tm.Add(key, v2.Map[key]);
                            else
                                tm[key] = v2.Map[key];
                        }
                        return v1;
                    }
                    else if (v2.Type == ValueType.STRING)
                        return new Value(v1.Map.ToString() + v2._string);
                    else
                        throw new RuntimeException("Bad type argument to +. Had {0} and {1}.", v1.Type, v2.Type);
                case ValueType.OBJECT:
                    if (v2.Type == ValueType.STRING)
                        return new Value(v1._object + v2._string);
                    else
                        throw new RuntimeException("Bad type argument to +. Had {0} and {1}.", v1.Type, v2.Type);
                case ValueType.STRING:
                    switch (v2.Type)
                    {
                        case ValueType.BOOL:
                            return new Value(v1._string + (v1.Bool==0?"False":"True"));
                        case ValueType.ARRAY:
                        case ValueType.DECIMAL:
                        case ValueType.INT:
                        case ValueType.LONG:
                        case ValueType.MAP:
                        case ValueType.OBJECT:
                        case ValueType.SHORT:
                        case ValueType.STRING:
                            return new Value(v1._string + v2.Raw.ToString());
                        default:
                            throw new RuntimeException("Bad type argument to +. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                default:
                    throw new RuntimeException("Bad type argument to +. Had {0} and {1}.", v1.Type, v2.Type);
            }
		}
        /// <summary>
        /// 
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        internal static Value Shl(Value v1, Value v2)
        {
            if (v1 == null && v2 == null)
                return Value.constI1_0;
            if (v1 == null)
                return v2;
            if (v2 == null)
                return v1;

            switch (v1.Type)
            {
                case ValueType.SHORT:
                case ValueType.INT:
                case ValueType.LONG:
                    switch (v2.Type)
                    {
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            return new Value(v1._number << (int)v2._number);
                        default:
                            throw new RuntimeException("Bad type argument to <<. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                default:
                    throw new RuntimeException("Bad type argument to <<. Had {0} and {1}.", v1.Type, v2.Type);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        internal static Value Shr(Value v1, Value v2)
        {
            if (v1 == null && v2 == null)
                return Value.constI1_0;
            if (v1 == null)
                return v2;
            if (v2 == null)
                return v1;

            switch (v1.Type)
            {
                case ValueType.SHORT:
                case ValueType.INT:
                case ValueType.LONG:
                    switch (v2.Type)
                    {
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            return new Value(v1._number >> (int)v2._number);
                        default:
                            throw new RuntimeException("Bad type argument to >>. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                default:
                    throw new RuntimeException("Bad type argument to >>. Had {0} and {1}.", v1.Type, v2.Type);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        internal static Value Div(Value v1, Value v2)
        {
            if (v1 == null && v2 == null)
                return Value.constI1_0;
            if (v1 == null)
                return v2;
            if (v2 == null)
                return v1;

            switch (v1.Type)
            {
                case ValueType.SHORT:
                case ValueType.INT:
                case ValueType.LONG:
                    switch (v2.Type)
                    {
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            return new Value(v1._number / v2._number);
                        case ValueType.DECIMAL:
                            return new Value(v1._number / v2._decimal);
                        default:
                            throw new RuntimeException("Bad type argument to /. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                case ValueType.DECIMAL:
                    switch (v2.Type)
                    {
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            return new Value(v1._decimal / v2._number);
                        case ValueType.DECIMAL:
                            return new Value(v1._decimal / v2._decimal);
                        default:
                            throw new RuntimeException("Bad type argument to /. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                default:
                    throw new RuntimeException("Bad type argument to /. Had {0} and {1}.", v1.Type, v2.Type);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        internal static Value Mul(Value v1, Value v2)
        {
            if (v1 == null && v2 == null)
                return Value.constI1_0;
            if (v1 == null)
                return v2;
            if (v2 == null)
                return v1;

            switch (v1.Type)
            {
                case ValueType.SHORT:
                case ValueType.INT:
                case ValueType.LONG:
                    switch (v2.Type)
                    {
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            return new Value(v1._number * v2._number);
                        case ValueType.DECIMAL:
                            return new Value(v1._number * v2._decimal);
                        default:
                            throw new RuntimeException("Bad type argument to *. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                case ValueType.DECIMAL:
                    switch (v2.Type)
                    {
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            return new Value(v1._decimal * v2._number);
                        case ValueType.DECIMAL:
                            return new Value(v1._decimal * v2._decimal);
                        default:
                            throw new RuntimeException("Bad type argument to *. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                default:
                    throw new RuntimeException("Bad type argument to *. Had {0} and {1}.", v1.Type, v2.Type);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        internal static Value Mod(Value v1, Value v2)
        {
            if (v1 == null && v2 == null)
                return Value.constI1_0;
            if (v1 == null)
                return v2;
            if (v2 == null)
                return v1;

            switch (v1.Type)
            {
                case ValueType.SHORT:
                case ValueType.INT:
                case ValueType.LONG:
                    switch (v2.Type)
                    {
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            return new Value(v1._number % v2._number);
                        case ValueType.DECIMAL:
                            return new Value(v1._number % v2._decimal);
                        default:
                            throw new RuntimeException("Bad type argument to %. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                case ValueType.DECIMAL:
                    switch (v2.Type)
                    {
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            return new Value(v1._decimal % v2._number);
                        case ValueType.DECIMAL:
                            return new Value(v1._decimal % v2._decimal);
                        default:
                            throw new RuntimeException("Bad type argument to %. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                default:
                    throw new RuntimeException("Bad type argument to %. Had {0} and {1}.", v1.Type, v2.Type);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <param name="_rt_val"></param>
        /// <returns></returns>
        internal static Value Cand(Value v1, Value v2)
        {
            return new Value(v1.IsTrueValue() && v2.IsTrueValue());
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <param name="_rt_val"></param>
        /// <returns></returns>
        internal static Value Cor(Value v1, Value v2)
        {
            return new Value(v1.IsTrueValue() || v2.IsTrueValue());
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <param name="_rt_val"></param>
        /// <returns></returns>
        internal static Value And(Value v1, Value v2)
        {
            if (v1 == null && v2 == null)
                return Value.constI1_0;
            if (v1 == null)
                return v2;
            if (v2 == null)
                return v1;

            switch (v1.Type)
            {
                case ValueType.BOOL:
                case ValueType.SHORT:
                case ValueType.INT:
                case ValueType.LONG:
                    switch (v2.Type)
                    {
                        case ValueType.BOOL:
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            return new Value(v1._number & v2._number);
                        default:
                            throw new RuntimeException("Bad type argument to &. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                default:
                    throw new RuntimeException("Bad type argument to &. Had {0} and {1}.", v1.Type, v2.Type);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <param name="_rt_val"></param>
        /// <returns></returns>
        internal static Value Xor(Value v1, Value v2)
        {
            if (v1 == null && v2 == null)
                return Value.constI1_0;
            if (v1 == null)
                return v2;
            if (v2 == null)
                return v1;

            switch (v1.Type)
            {
                case ValueType.BOOL:
                case ValueType.SHORT:
                case ValueType.INT:
                case ValueType.LONG:
                    switch (v2.Type)
                    {
                        case ValueType.BOOL:
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            return new Value(v1._number ^ v2._number);
                        default:
                            throw new RuntimeException("Bad type argument to ^. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                default:
                    throw new RuntimeException("Bad type argument to ^. Had {0} and {1}.", v1.Type, v2.Type);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <param name="_rt_val"></param>
        /// <returns></returns>
        internal static Value Or(Value v1, Value v2)
        {
            if (v1 == null && v2 == null)
                return Value.constI1_0;
            if (v1 == null)
                return v2;
            if (v2 == null)
                return v1;

            switch (v1.Type)
            {
                case ValueType.BOOL:
                case ValueType.SHORT:
                case ValueType.INT:
                case ValueType.LONG:
                    switch (v2.Type)
                    {
                        case ValueType.BOOL:
                        case ValueType.SHORT:
                        case ValueType.INT:
                        case ValueType.LONG:
                            return new Value(v1._number | v2._number);
                        default:
                            throw new RuntimeException("Bad type argument to |. Had {0} and {1}.", v1.Type, v2.Type);
                    }
                default:
                    throw new RuntimeException("Bad type argument to |. Had {0} and {1}.", v1.Type, v2.Type);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return this.Type.GetHashCode() ^ this.Raw.GetHashCode();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;
            Value other = obj as Value;
            if (other == null)
                return false;
            if (this.Type != other.Type)
                return false;

            switch (this.Type)
            {
                case ValueType.BOOL:
                case ValueType.DECIMAL:
                case ValueType.INT:
                case ValueType.LONG:
                case ValueType.SHORT:
                    return this.Raw == other.Raw;
                case ValueType.MAP:
                    return this.Map.Equals(other.Map);
                case ValueType.ARRAY:
                    return this.Array.Equals(other.Array);
                case ValueType.OBJECT:
                    return this.Object.Id == other.Object.Id;
                case ValueType.STRING:
                    return this.String.Equals(other.String);
            }

            return false;
        }
        /// <summary>
        /// 
        /// </summary>
        public int Size
        {
            get
            {
                switch (this._type)
                {
                    case ValueType.ARRAY: return this.Array.Count;
                    case ValueType.BOOL: return sizeof(bool);
                    case ValueType.DECIMAL: return sizeof(decimal);
                    case ValueType.INT: return sizeof(int);
                    case ValueType.LONG: return sizeof(long);
                    case ValueType.MAP: return this.Map.Count;
                    case ValueType.SHORT: return sizeof(short);
                    case ValueType.STRING: return this.String.Length;
                }
                return 0;
            }
        }
    }

	public enum ValueType
	{
		INVALID,
		DECIMAL,
		STRING,
		OBJECT,
		BOOL,
		SHORT,
		INT,
		LONG,
        MAP,
        ARRAY
	}
}
